package com.eatme.web.security.jwt.filter;

import com.eatme.web.config.AppProperties;
import com.eatme.web.util.CollectionUtil;
import com.eatme.web.util.jwt.NimbusJoseJwtUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.nimbusds.jwt.JWTClaimsSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotNull;
import java.io.IOException;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

// @Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {



    private static final Logger LOG = LoggerFactory.getLogger(JwtAuthenticationFilter.class);

    @Autowired
    AppProperties appProperties;
    @Autowired
    private ObjectMapper objectMapper;
    @Autowired
    private NimbusJoseJwtUtil nimbusJoseJwtUtil;

    @Override
    protected void doFilterInternal(
            @NotNull HttpServletRequest request,
            @NotNull HttpServletResponse response,
            @NotNull FilterChain filterChain
    ) throws IOException, ServletException {
        if (checkToken(request)) {
//            try {
                nimbusJoseJwtUtil.validateToken(request).filter(jWTClaimsSet -> jWTClaimsSet.getClaim("scope") != null)
                        // 有值处理，空处理
                        .ifPresentOrElse(this::setUpSpringAuthentication, SecurityContextHolder::clearContext);
//            } catch (ExpiredJwtException e) {
//                response.setStatus(HttpStatus.OK.value());
//                response.setContentType(MediaType.APPLICATION_JSON_VALUE);
//                response.setCharacterEncoding("UTF-8");
//                response.getWriter().println(objectMapper.writeValueAsString(AppResponse.setResult(ResponseCode.ACCESS_TOKEN_INVALID)));
//                return;
//            }
        }
        filterChain.doFilter(request, response);
    }

    /**
     * 构造 Authentication
     *
     * @param claims {@link JWTClaimsSet}
     */
    private void setUpSpringAuthentication(JWTClaimsSet claims) {
        Optional.of(claims.getClaim("scope")).map(o -> {
                    List<?> rawList = (List<String>) CollectionUtil.convertObjectToList(o);
                    // 获取用户角色，注意 "scope" 要与生成 token 时的保持一致
                    List authorities = rawList.stream().map(String::valueOf).map(SimpleGrantedAuthority::new).collect(Collectors.toList());
                    // TODO 通用 auth 对象
                    return new UsernamePasswordAuthenticationToken(
                            claims.getSubject(),
                            null,
                            authorities);
                })
                // 设置到Spring Security中，用于后面的校验(这步是关键)
                .ifPresentOrElse(auth -> SecurityContextHolder.getContext().setAuthentication(auth), SecurityContextHolder::clearContext);
    }

    /**
     * 检查 JWT Token 是否在 HTTP 报头中
     *
     * @param req HTTP 请求
     * @return 是否有 JWT Token
     */
    private boolean checkToken(HttpServletRequest req) {
        final String authenticationHeader = req.getHeader(appProperties.getJwt().getHeader());
        return authenticationHeader != null && authenticationHeader.startsWith(appProperties.getJwt().getPrefix());
    }
}
